
#ifndef VOICE_COMMON_OUTPUT_H
#define VOICE_COMMON_OUTPUT_H

#ifndef VOICE_COMMON_H
#error "Includes voice_common.h instead of voice_output.h!"
#endif

/*
 * voice output resource partition
 *   header
 *   meta voice attr table
 *   meta voice data table
 */
#define VOICE_OUTPUT_PART_MAGIC	0x01CE3A1C

typedef struct _voice_output_partition_header {
	uint32_t	magic;
	uint32_t	meta_nums;	/* meta voice number */
	uint32_t	meta_data_totalsize;	/* unit in bytes */
	uint32_t	reserved;
} voice_output_partition_header_s;
#define VOICE_OUTPUT_PARTITION_HEADER_SIZE	sizeof(voice_output_partition_header_s)

/*
 * maxsize of meta voice
 */
#ifdef GKT_CONFIG_VOICE_OUTPUT_META_MAXSIZE
#define VOICE_OUTPUT_META_MAXSIZE	GKT_CONFIG_VOICE_OUTPUT_META_MAXSIZE
#else
#define VOICE_OUTPUT_META_MAXSIZE	0xFFFF
#endif
#define VOICE_OUTPUT_META_DATA_TOTALSIZE	\
	(GKT_VOICE_OUTPUT_META_MAXNUMS * VOICE_OUTPUT_META_MAXSIZE)

typedef struct _voice_output_meta_attr {
	uint32_t	data_offset;
#if (VOICE_OUTPUT_META_MAXSIZE < 0x10000)
	uint16_t	data_size;	/* unit in bytes */
#else
	uint32_t	data_size;
#endif
#if !GKT_VOICE_DEC_INPUT_FRAME_SIZE || !GKT_CONFIG_VOICE_OUTPUT_RES_MAP_READ
	uint16_t	frame_nums;
#endif
} voice_output_meta_attr_s;
#define VOICE_OUTPUT_META_ATTR_SIZE	sizeof(voice_output_meta_attr_s)

typedef enum {
	VOICE_OUTPUT_ST_BLOCK_END = 0,
	VOICE_OUTPUT_ST_END,
	VOICE_OUTPUT_ST_ERROR,
	VOICE_OUTPUT_ST_INVALID
} voice_output_status_e;

typedef struct _voice_output {
	gkt_mutex_t mutex;
	gkt_semaphore_t semaphore;

//	const gkt_flash_partition_info_s *c_flash_info;
	const voice_output_partition_header_s *c_header;
	const voice_output_meta_attr_s	*c_meta_attrs;
	union {
		const uint8_t	*c_meta_data_base;
		uint32_t	meta_data_base_offset;
	};

	volatile uint32_t	state;
	voice_output_status_e	status;

	gkt_soft_timer_t	timer_id;	/* multi-playback timer */
	gkt_voice_output_callback_fn	callback;
	
	const gkt_voice_combined_attr_s *combined_attr;
	gkt_voice_output_playback_attr_s	playback_attr;
	uint32_t	private_data;

#if !GKT_VOICE_OUTPUT_VOLUME_DISABLE
	gkt_voice_output_volume_e	volume;
#endif

	uint32_t	meta_id_index;
	uint32_t	total_frame_nums;
	uint32_t	total_left_frame_nums;
	uint32_t	meta_left_frame_nums;

	const uint8_t	*meta_data;
	uint8_t	*meta_buffer;

	uint32_t	meta_buffer_size;

	uint32_t	block_index;
	uint8_t	*dec_buffer;	/* dec blocks */
} voice_output_s;

/************************************************************
 * meta data format
 ************************************************************  
 *   FIELDS     BYTES      NOTES
 * --------------------------------------------------
 *  frame_nums    2        frame nums of meta data
 *  frames        N        frames of meta data
 ************************************************************
 *
 ************************************************************ 
 * meta frame format
 ************************************************************ 
 *   FIELDS     BYTES      NOTES
 * --------------------------------------------------
 *  frame_size    1        size of frame, unit in bytes
 *  frame_data    M        data of frame
 ************************************************************/


/************************************************************
 * sampling_point_bytes
 ************************************************************/
#define VOICE_OUTPUT_DEC_SP_BYTES	\
	((GKT_VOICE_DEC_SAMPLE_POINT_BITS + 7) >> 3)

/************************************************************
 * sampling_point_nums
 ************************************************************/
#define VOICE_OUTPUT_DEC_FRAME_SP_NUMS	\
	GKT_VOICE_DEC_OUTPUT_FRAME_SIZE

/************************************************************
 * dec_frame_bytes = sampling_point_bytes * sampling_point_nums
 *  - sampling_point_bytes : bytes per sampling point
 *  - sampling_point_nums  : number of sampling points per frame
 ************************************************************/
#define VOICE_OUTPUT_DEC_FRAME_BYTES	\
	(VOICE_OUTPUT_DEC_SP_BYTES * VOICE_OUTPUT_DEC_FRAME_SP_NUMS)

/************************************************************
 * frames transition during decoding
 ************************************************************/
#if defined(GKT_CONFIG_VOICE_OUTPUT_HARDWARE_B2206) \
	&& GKT_CONFIG_VOICE_OUTPUT_HARDWARE_B2206
#define VOICE_OUTPUT_APPEND_FRAMES	0
#elif defined(GKT_CONFIG_VOICE_OUTPUT_APPEND_FRAMES)
#define VOICE_OUTPUT_APPEND_FRAMES	GKT_CONFIG_VOICE_OUTPUT_APPEND_FRAMES
#else
#define VOICE_OUTPUT_APPEND_FRAMES	2
#endif
#define VOICE_OUTPUT_APPEND_BYTES	\
	(VOICE_OUTPUT_DEC_FRAME_BYTES * VOICE_OUTPUT_APPEND_FRAMES)
#define VOICE_OUTPUT_APPEND_TIME	\
	(GKT_VOICE_DEC_FRAME_DURATION * VOICE_OUTPUT_APPEND_FRAMES)

#if (VOICE_OUTPUT_APPEND_FRAMES > 0)
/* head cut-off time, should <= VOICE_OUTPUT_APPEND_TIME */
#ifdef GKT_CONFIG_VOICE_OUTPUT_HEAD_CUTOFF_TIME
#define VOICE_OUTPUT_HEAD_CUTOFF_TIME	\
	GKT_CONFIG_VOICE_OUTPUT_HEAD_CUTOFF_TIME
#if (VOICE_OUTPUT_HEAD_CUTOFF_TIME > VOICE_OUTPUT_APPEND_TIME)
#error "HEAD_CUTOFF_TIME should <= APPEND_FRAMES_TIME"
#endif
#else
#define VOICE_OUTPUT_HEAD_CUTOFF_TIME	\
	(VOICE_OUTPUT_APPEND_TIME / 2)
#endif

/* tail cut-off time, should <= VOICE_OUTPUT_APPEND_TIME */
#ifdef GKT_CONFIG_VOICE_OUTPUT_TAIL_CUTOFF_TIME
#define VOICE_OUTPUT_TAIL_CUTOFF_TIME	\
	GKT_CONFIG_VOICE_OUTPUT_TAIL_CUTOFF_TIME
#if (VOICE_OUTPUT_TAIL_CUTOFF_TIME > VOICE_OUTPUT_APPEND_TIME)
#error "TAIL_CUTOFF_TIME should <= APPEND_FRAMES_TIME"
#endif
#else
#define VOICE_OUTPUT_TAIL_CUTOFF_TIME	0
#endif
#else
#define VOICE_OUTPUT_HEAD_CUTOFF_TIME	0
#define VOICE_OUTPUT_TAIL_CUTOFF_TIME	0
#endif

/* total cut-off time, should <= VOICE_OUTPUT_APPEND_TIME */
#define VOICE_OUTPUT_TOTAL_CUTOFF_TIME	\
	(VOICE_OUTPUT_HEAD_CUTOFF_TIME + VOICE_OUTPUT_TAIL_CUTOFF_TIME)
#if (VOICE_OUTPUT_TOTAL_CUTOFF_TIME > VOICE_OUTPUT_APPEND_TIME)
#error "TOTAL_CUTOFF_TIME should <= APPEND_FRAMES_TIME"
#endif

#if !VOICE_OUTPUT_TOTAL_CUTOFF_TIME
#define VOICE_OUTPUT_DEC_BUFFER_APPEND_FRAMES	0
#define VOICE_OUTPUT_DEC_BUFFER_APPEND_BYTES	0
#define VOICE_OUTPUT_DEC_HEAD_CUTOFF_BYTES	0
#define VOICE_OUTPUT_DEC_TAIL_CUTOFF_BYTES	0
#define VOICE_OUTPUT_DEC_MIDD_JOINT_BYTES	0
#define VOICE_OUTPUT_DEC_TAIL_JOINT_BYTES	0
#elif !VOICE_OUTPUT_HEAD_CUTOFF_TIME
#define VOICE_OUTPUT_DEC_BUFFER_APPEND_FRAMES	0
#define VOICE_OUTPUT_DEC_BUFFER_APPEND_BYTES	0
#define VOICE_OUTPUT_DEC_HEAD_CUTOFF_BYTES	0
#define VOICE_OUTPUT_DEC_TAIL_CUTOFF_BYTES	\
	((VOICE_OUTPUT_APPEND_BYTES * VOICE_OUTPUT_TAIL_CUTOFF_TIME) \
		/ VOICE_OUTPUT_APPEND_TIME)
#define VOICE_OUTPUT_DEC_MIDD_JOINT_BYTES	0
#define VOICE_OUTPUT_DEC_TAIL_JOINT_BYTES	0
#else
#define VOICE_OUTPUT_DEC_BUFFER_APPEND_FRAMES	\
	VOICE_OUTPUT_APPEND_FRAMES
#define VOICE_OUTPUT_DEC_BUFFER_APPEND_BYTES	\
	VOICE_OUTPUT_APPEND_BYTES
#define VOICE_OUTPUT_DEC_HEAD_CUTOFF_BYTES	\
	((VOICE_OUTPUT_DEC_BUFFER_APPEND_BYTES * VOICE_OUTPUT_HEAD_CUTOFF_TIME) \
		/ VOICE_OUTPUT_APPEND_TIME)
#define VOICE_OUTPUT_DEC_TAIL_CUTOFF_BYTES	\
	((VOICE_OUTPUT_DEC_BUFFER_APPEND_BYTES * VOICE_OUTPUT_TAIL_CUTOFF_TIME) \
		/ VOICE_OUTPUT_APPEND_TIME)
#define VOICE_OUTPUT_DEC_MIDD_JOINT_BYTES	\
	(VOICE_OUTPUT_DEC_BUFFER_APPEND_BYTES - VOICE_OUTPUT_DEC_HEAD_CUTOFF_BYTES)
#define VOICE_OUTPUT_DEC_TAIL_JOINT_BYTES	\
	(VOICE_OUTPUT_DEC_BUFFER_APPEND_BYTES - VOICE_OUTPUT_DEC_HEAD_CUTOFF_BYTES \
		- VOICE_OUTPUT_DEC_TAIL_CUTOFF_BYTES)
#endif
#define VOICE_OUTPUT_DEC_TOTAL_CUTOFF_BYTES	\
	(VOICE_OUTPUT_DEC_HEAD_CUTOFF_BYTES + VOICE_OUTPUT_DEC_TAIL_CUTOFF_BYTES)


/************************************************************
 * a complete decoded voice might be too long to be stored,
 * so we split the input/encoded voice to some blocks with
 * proper size, decrypting and playing the decoded voice blocks.
 ************************************************************/
#ifdef GKT_CONFIG_VOICE_OUTPUT_DEC_BLOCK_NUMS
#define VOICE_OUTPUT_DEC_BLOCK_NUMS	GKT_CONFIG_VOICE_OUTPUT_DEC_BLOCK_NUMS
#else
#define VOICE_OUTPUT_DEC_BLOCK_NUMS	2
#endif

#ifdef GKT_CONFIG_VOICE_OUTPUT_DEC_BLOCK_FRAMES
#define VOICE_OUTPUT_DEC_BLOCK_FRAMES	GKT_CONFIG_VOICE_OUTPUT_DEC_BLOCK_FRAMES
#else
#define VOICE_OUTPUT_DEC_BLOCK_FRAMES	10
#endif

#define VOICE_OUTPUT_DEC_BLOCK_DURATION	\
	VOICE_OUTPUT_DEC_BLOCK_FRAMES * GKT_VOICE_DEC_FRAME_DURATION

/************************************************************
 * dec_block_bytes = dec_frame_bytes * frame_nums
 *  - dec_frame_bytes : bytes per dec frame (DEC_FRAME_BYTES)
 *  - frame_nums      : number of dec frames per block
 ************************************************************/
#define VOICE_OUTPUT_DEC_BLOCK_BYTES	\
	(VOICE_OUTPUT_DEC_FRAME_BYTES * VOICE_OUTPUT_DEC_BLOCK_FRAMES)
#define VOICE_OUTPUT_DEC_BUFFER_SIZE	\
	(VOICE_OUTPUT_DEC_BLOCK_BYTES * GKT_VOICE_DEC_CONVERSION_SAMPLE_MULTIPLE * VOICE_OUTPUT_DEC_BLOCK_NUMS)
#define VOICE_OUTPUT_ENC_META_DATA_SIZE \
	(GKT_VOICE_DEC_INPUT_FRAME_SIZE * VOICE_OUTPUT_DEC_BLOCK_FRAMES * VOICE_OUTPUT_DEC_BLOCK_NUMS)
#ifdef GKT_CONFIG_VOICE_OUTPUT_ENC_ALL_META_DATA_SIZE
#define VOICE_OUTPUT_ENC_ALL_META_DATA_SIZE GKT_CONFIG_VOICE_OUTPUT_ENC_ALL_META_DATA_SIZE
#else
#define VOICE_OUTPUT_ENC_ALL_META_DATA_SIZE (10*1024)
#endif
#ifdef GKT_CONFIG_VOICE_OUTPUT_DEC_BLOCK_BUFFER_STATIC
#define VOICE_OUTPUT_DEC_BLOCK_BUFFER_STATIC	GKT_CONFIG_VOICE_OUTPUT_DEC_BLOCK_BUFFER_STATIC
#else
#define VOICE_OUTPUT_DEC_BLOCK_BUFFER_STATIC	0
#endif
#ifdef GKT_CONFIG_VOICE_OUTPUT_ENC_META_BUFFER_STATIC
#define VOICE_OUTPUT_ENC_META_BUFFER_STATIC	GKT_CONFIG_VOICE_OUTPUT_ENC_META_BUFFER_STATIC
#else
#define VOICE_OUTPUT_ENC_META_BUFFER_STATIC	0
#endif

/* voice output state */
#define VOICE_OUTPUT_STATE_PLAY_REQ	(1U << 0)
#define VOICE_OUTPUT_STATE_PLAY_START	(1U << 4)
#define VOICE_OUTPUT_STATE_TIMER_START	(1U << 8)
#define VOICE_OUTPUT_STATE_TIMER_EXPIRE	(1U << 9)
#define VOICE_OUTPUT_STATE_TIMER_MASK	\
	(VOICE_OUTPUT_STATE_TIMER_START | VOICE_OUTPUT_STATE_TIMER_EXPIRE)

#ifdef GKT_CONFIG_VOICE_OUTPUT_SYNC_TIMEOUT
#define VOICE_OUTPUT_SYNC_TIMEOUT	GKT_CONFIG_VOICE_OUTPUT_SYNC_TIMEOUT
#else
#define VOICE_OUTPUT_SYNC_TIMEOUT	500
#endif

/* decode */
#if VOICE_OUTPUT_APPEND_FRAMES > 0
void voice_output_decode_append_frames(voice_output_s *output);
#else
#define voice_output_decode_append_frames(output)	\
	do {} while (0)
#endif
int voice_output_decode_blocks(voice_output_s *output);

/* resource */
void voice_output_sync_resource(voice_output_s *output);
int voice_output_check_resource(voice_output_s *output);
void voice_output_next_meta(voice_output_s *output);
uint32_t voice_output_get_all_meta_data(voice_output_s *output);
uint32_t voice_output_get_total_frame_nums(voice_output_s *output,
				const gkt_voice_combined_attr_s *combined_attr);

/* hw output*/
int voice_output_hw_init(voice_output_s *output);
int voice_output_hw_start(uint8_t volume_level, const uint8_t *data, uint32_t size);
void voice_output_hw_stop(void);

/* common */
int voice_output_init(void);
void voice_output_status_notify(voice_output_status_e status);

#endif

